﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace WokseleNaDrzewie
{
    public class Octree
    {
        public double CubeSize { get; set; }
        public Point3D Origin { get; set; }
        public Node Root { get; set; }
        private List<Triangle> triangles;
        private double eps;
        public ModelVisual3D ModelVisual { get; set; }
        private Model3DGroup modelGroup;

        public Octree(double cubeSize, Point3D origin, List<Triangle> _triangles, bool isConvex, int divide)
        {
            CubeSize = cubeSize;
            Origin = origin;
            triangles = _triangles;
            eps = cubeSize / divide;
            Root = new Node(new Rect3D(Origin, new Size3D(CubeSize, CubeSize, CubeSize)));

            ModelVisual = new ModelVisual3D();
            modelGroup = new Model3DGroup();
            DirectionalLight dirLight = new DirectionalLight(Colors.White, new Vector3D(1, 1, 1));
            DirectionalLight sideLight = new DirectionalLight(Colors.White, new Vector3D(-1, -1, -1));
            modelGroup.Children.Add(dirLight);
            modelGroup.Children.Add(sideLight);

            if (isConvex)
            {
                if (!Convex.isInside(Root.bounds, triangles))
                    divideCube(Root, true);
                else
                {
                    DiffuseMaterial difMat = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
                    MeshGeometry3D mesh = new MeshGeometry3D();
                    Point3D location = Root.bounds.Location;
                    double sizex = Root.bounds.SizeX;
                    mesh.Positions.Add(location);
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z));
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z));
                    mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z));
                    mesh.Positions.Add(new Point3D(location.X, location.Y, location.Z + sizex));
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z + sizex));
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z + sizex));
                    mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z + sizex));
                    mesh.TriangleIndices = createIndices(mesh);
                    GeometryModel3D modelGeometry = new GeometryModel3D(mesh, difMat);
                    modelGroup.Children.Add(modelGeometry);
                }
            }
            else
                if (!NonConvex.isInside(Root.bounds, triangles))
                    divideCube(Root, false);
                else
                {
                    DiffuseMaterial difMat = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
                    MeshGeometry3D mesh = new MeshGeometry3D();
                    Point3D location = Root.bounds.Location;
                    double sizex = Root.bounds.SizeX;
                    mesh.Positions.Add(location);
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z));
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z));
                    mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z));
                    mesh.Positions.Add(new Point3D(location.X, location.Y, location.Z + sizex));
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z + sizex));
                    mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z + sizex));
                    mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z + sizex));
                    mesh.TriangleIndices = createIndices(mesh);
                    GeometryModel3D modelGeometry = new GeometryModel3D(mesh, difMat);
                    modelGroup.Children.Add(modelGeometry);
                }


            ModelVisual.Content = modelGroup;
        }

        private void divideCube(Node r, bool isConvex)
        {
            //dziel na 8 i dodaj do childów
            Rect3D big = r.bounds;
            Size3D size = new Size3D(big.SizeX / 2, big.SizeY / 2, big.SizeZ / 2);
            if (size.X < eps)
                return;
            r.children[0] = new Node(new Rect3D(big.Location, size));
            r.children[1] = new Node(new Rect3D(new Point3D(big.Location.X + size.X, big.Location.Y, big.Location.Z), size));
            r.children[2] = new Node(new Rect3D(new Point3D(big.Location.X, big.Location.Y + size.Y, big.Location.Z), size));
            r.children[3] = new Node(new Rect3D(new Point3D(big.Location.X, big.Location.Y, big.Location.Z + size.Z), size));
            r.children[4] = new Node(new Rect3D(new Point3D(big.Location.X + size.X, big.Location.Y + size.Y, big.Location.Z), size));
            r.children[5] = new Node(new Rect3D(new Point3D(big.Location.X + size.X, big.Location.Y, big.Location.Z + size.Z), size));
            r.children[6] = new Node(new Rect3D(new Point3D(big.Location.X, big.Location.Y + size.Y, big.Location.Z + size.Z), size));
            r.children[7] = new Node(new Rect3D(new Point3D(big.Location.X + size.X, big.Location.Y + size.Y, big.Location.Z + size.Z), size));
            //sprawdzaj po podziale
            foreach (Node n in r.children)
                if (isConvex)
                {
                    if (!Convex.isInside(n.bounds, triangles))
                        divideCube(n, true);
                    else
                    {
                        DiffuseMaterial difMat = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
                        MeshGeometry3D mesh = new MeshGeometry3D();
                        Point3D location = n.bounds.Location;
                        double sizex = n.bounds.SizeX;
                        mesh.Positions.Add(location);
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z));
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z));
                        mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z));
                        mesh.Positions.Add(new Point3D(location.X, location.Y, location.Z + sizex));
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z + sizex));
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z + sizex));
                        mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z + sizex));
                        mesh.TriangleIndices = createIndices(mesh);
                        GeometryModel3D modelGeometry = new GeometryModel3D(mesh, difMat);
                        modelGroup.Children.Add(modelGeometry);
                    }
                }
                else
                    if (!NonConvex.isInside(n.bounds, triangles))
                        divideCube(n, false);
                    else
                    {
                        DiffuseMaterial difMat = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
                        MeshGeometry3D mesh = new MeshGeometry3D();
                        Point3D location = n.bounds.Location;
                        double sizex = n.bounds.SizeX;
                        mesh.Positions.Add(location);
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z));
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z));
                        mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z));
                        mesh.Positions.Add(new Point3D(location.X, location.Y, location.Z + sizex));
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y, location.Z + sizex));
                        mesh.Positions.Add(new Point3D(location.X + sizex, location.Y + sizex, location.Z + sizex));
                        mesh.Positions.Add(new Point3D(location.X, location.Y + sizex, location.Z + sizex));
                        mesh.TriangleIndices = createIndices(mesh);
                        GeometryModel3D modelGeometry = new GeometryModel3D(mesh, difMat);
                        modelGroup.Children.Add(modelGeometry);
                    }
        }

        private Int32Collection createIndices(MeshGeometry3D mesh)
        {
            Int32Collection ind = new Int32Collection();

            //przód
            ind.Add(2); ind.Add(1); ind.Add(0);
            ind.Add(3); ind.Add(2); ind.Add(0);

            //tył
            ind.Add(6); ind.Add(4); ind.Add(5);
            ind.Add(6); ind.Add(7); ind.Add(4);

            //góra
            ind.Add(6); ind.Add(2); ind.Add(3);
            ind.Add(7); ind.Add(6); ind.Add(3);

            //dół
            ind.Add(5); ind.Add(0); ind.Add(1);
            ind.Add(4); ind.Add(0); ind.Add(5);

            //lewo
            ind.Add(7); ind.Add(3); ind.Add(0);
            ind.Add(4); ind.Add(7); ind.Add(0);

            //prawo
            ind.Add(5); ind.Add(1); ind.Add(2);
            ind.Add(6); ind.Add(5); ind.Add(2);

            return ind;
        }
    }
}
